import { _decorator, Component, Game, Node, UITransform, v3, Vec3 } from 'cc';
import { PalBattleHero, PalBattleSkill, PalBattleSystem } from './PalBattleSystem';
import { DataManager } from './DataManager';
import { EnemyEnum } from '../data/enemy/EnemyEnum';
import { HeroEnum } from '../data/hero/HeroEnum';
import { PlayerManager } from './PlayerManager';
import { UiManager } from './UiManager';
import { ActionQueue, ActionQueueItem } from './ActionQueue';
import { EnemyItem } from './EnemyItem';
import { config } from './Setting';
const { ccclass, property } = _decorator;

export enum GameActionQueueEnum {
    Bullet = 'Bullet',
    Damage = 'Damage',
    SetHeroTurn = 'SetHeroTurn',
    SetEnemyTurn = 'SetEnemyTurn',
    HeroAttack = 'HeroAttack',
    EnemyAttack = 'EnemyAttack',
    SetHeroStatus = 'SetHeroStatus',
    SetEnemyStatus = 'SetEnemyStatus',
    HeroDead = 'HeroDead',
    EnemyDead = 'EnemyDead',
    HeroAnime = 'HeroAnime', // 播放英雄动画
}

@ccclass('GameManager')
export class GameManager extends Component {
    public static instance = null;
    public static getInstance<GameManager>(): GameManager {
        if (!this.instance) this.instance = new this() as GameManager;
        return this.instance;
    }
    installSingletion() {
        if (!GameManager.instance) {
            GameManager.instance = this;
        }
    }

    callables: {
        [key in string]: (args: {
            args: any;
            content: any;
            actionQueue: ActionQueueItem;
            totalDuration: number;
            actionIndex: number;
        }) => void
    } = {
            [GameActionQueueEnum.HeroAnime]: (args: {
                index: number;
                args: { UUID: string; name: string; };
                content: any;
                actionQueue: ActionQueueItem;
                totalDuration: number;
                actionIndex: number;
            }) => {
                const uiManager = (UiManager.instance as UiManager);
                console.log('播放动画', args);
            },
            [GameActionQueueEnum.Bullet]: (args: {
                index: number;
                args: { from: Node; to: Node[]; atk: number };
                content: any;
                actionQueue: ActionQueueItem;
                totalDuration: number;
                actionIndex: number;
            }) => {
                const uiManager = (UiManager.instance as UiManager);
                uiManager.bullet(args.args.from.worldPosition, args.args.to.map(to => to.worldPosition));
            },
            [GameActionQueueEnum.Damage]: (args: {
                index: number;
                args: { worldPosition: Vec3; atk: number };
                content: any;
                actionQueue: ActionQueueItem;
                totalDuration: number;
                actionIndex: number;
            }) => {
                const uiManager = (UiManager.instance as UiManager);
                uiManager.damage(args.args.worldPosition, args.args.atk);
            },
            [GameActionQueueEnum.SetHeroTurn]: (args: {
                args: {};
                content: any;
                actionQueue: ActionQueueItem;
                totalDuration: number;
            }) => {
                setTimeout(() => {
                    (GameManager.instance as GameManager).palBattleSystem.nextTurn();
                }, 205);
            },
            [GameActionQueueEnum.HeroAttack]: (args: {
                args: { fromUUID: string; toUUID: string };
                content: any;
                actionQueue: ActionQueueItem;
                totalDuration: number;
                actionIndex: number;
            }) => {
                const gameManager = (GameManager.instance as GameManager);
                const uiManager = (UiManager.instance as UiManager);
                const system = gameManager.palBattleSystem;
                let enemyNode = uiManager.getEnemyNode(args.args.toUUID);
                let toWorldPosition = enemyNode.worldPosition;
                if (system.getHeroDie(args.args.fromUUID)) return;
                if (system.getEnemyDie(args.args.toUUID)) {
                    const randEnemy = system.getRandomEnemy();
                    if (!randEnemy) {
                        console.log('win');
                        system.actionQueue.stop();
                        return;
                    }
                    const toUUID = randEnemy.saveData.UUID;
                    enemyNode = uiManager.getEnemyNode(toUUID);
                    toWorldPosition = v3(enemyNode.worldPosition.x, enemyNode.worldPosition.y - (enemyNode.getComponent(UITransform) as UITransform).height / 2, 0);
                    const atk = system.getHeroAtk(args.args.fromUUID);
                    const actions = [
                        {
                            action: GameActionQueueEnum.HeroAttack,
                            duration: config.attackSpeed,
                            args: {
                                fromUUID: args.args.fromUUID,
                                toUUID,
                            }
                        },
                        {
                            action: GameActionQueueEnum.Damage,
                            duration: config.attackSpeed,
                            args: {
                                worldPosition: toWorldPosition,
                                atk,
                            }
                        },
                        {
                            action: GameActionQueueEnum.SetEnemyStatus,
                            duration: 0,
                            args: {
                                UUID: toUUID,
                                status: 'hp',
                                value: () => system.getEnemyHp(toUUID) - atk,
                            }
                        },
                    ];
                    system.actionQueue.override(args.actionIndex, {
                        actions: actions,
                        flag: ''
                    }).add({
                        actions: [{
                            duration: 0,
                            action: GameActionQueueEnum.SetHeroTurn,
                            args: {}
                        }],
                        flag: ""
                    });
                    return;
                }
                uiManager.heroAttack(args.args.fromUUID, toWorldPosition);
            },
            [GameActionQueueEnum.EnemyAttack]: (args: {
                args: { fromUUID: string; toUUID: string };
                content: any;
                actionQueue: ActionQueueItem;
                totalDuration: number;
                actionIndex: number;
            }) => {
                const gameManager = (GameManager.instance as GameManager);
                const uiManager = (UiManager.instance as UiManager);
                const system = gameManager.palBattleSystem;
                let heroNode = uiManager.getHeroNode(args.args.toUUID);
                let toWorldPosition = heroNode.worldPosition;
                if (system.getEnemyDie(args.args.fromUUID)) return;
                if (system.getHeroDie(args.args.toUUID)) {
                    const randHero = system.getRandomHero();
                    if (!randHero) {
                        console.log('win');
                        system.actionQueue.stop();
                        return;
                    }
                    const toUUID = randHero.saveData.UUID;
                    heroNode = uiManager.getHeroNode(toUUID);
                    toWorldPosition = v3(heroNode.worldPosition.x, heroNode.worldPosition.y - (heroNode.getComponent(UITransform) as UITransform).height / 2, 0);
                    const atk = system.getEnemyAtk(args.args.fromUUID);
                    const actions = [
                        {
                            action: GameActionQueueEnum.EnemyAttack,
                            duration: config.attackSpeed,
                            args: {
                                fromUUID: args.args.fromUUID,
                                toUUID,
                            }
                        },
                        {
                            action: GameActionQueueEnum.Damage,
                            duration: config.attackSpeed,
                            args: {
                                worldPosition: toWorldPosition,
                                atk,
                            }
                        },
                        {
                            action: GameActionQueueEnum.SetHeroStatus,
                            duration: 0,
                            args: {
                                UUID: toUUID,
                                status: 'hp',
                                value: () => system.getHeroHp(toUUID) - atk,
                            }
                        },
                    ];
                    system.actionQueue.override(args.actionIndex, {
                        actions: actions,
                        flag: ''
                    }).add({
                        actions: [{
                            duration: 0,
                            action: GameActionQueueEnum.SetEnemyTurn,
                            args: {}
                        }],
                        flag: ""
                    });
                    return;
                }
                uiManager.enemyAttack(args.args.fromUUID, toWorldPosition);
            },
            [GameActionQueueEnum.SetHeroStatus]: (args: {
                args: { UUID: string; status: string; value: any | (() => any) };
                content: any;
                actionQueue: ActionQueueItem;
                totalDuration: number;
                actionIndex: number;
            }) => {
                const { UUID, status, value } = args.args;
                const uiManager = (UiManager.instance as UiManager);
                const gameManager = (GameManager.instance as GameManager);
                const _value = typeof value === 'function' ? value() : value;
                gameManager.palBattleSystem.setHeroStatus(UUID, status, _value);
                uiManager.heroStatusChange(UUID, status, _value);
            },
            [GameActionQueueEnum.SetEnemyStatus]: (args: {
                args: { UUID: string; status: string; value: any | (() => any) };
                content: any;
                actionQueue: ActionQueueItem;
                totalDuration: number;
            }) => {
                const { UUID, status, value } = args.args;
                const uiManager = (UiManager.instance as UiManager);
                const gameManager = (GameManager.instance as GameManager);
                const _value = typeof value === 'function' ? value() : value;
                gameManager.palBattleSystem.setEnemyStatus(UUID, status, _value);
                uiManager.enemyStatusChange(UUID, status, _value);
            },
            [GameActionQueueEnum.HeroDead]: (args: {
                args: { UUID: string };
                content: any;
                actionQueue: ActionQueueItem;
                totalDuration: number;
            }) => {
                const { UUID } = args.args;
                const uiManager = (UiManager.instance as UiManager);
                uiManager.heroDead(UUID);
            },
            [GameActionQueueEnum.EnemyDead]: (args: {
                args: { UUID: string };
                content: any;
                actionQueue: ActionQueueItem;
                totalDuration: number;
            }) => {
                const { UUID } = args.args;
                const uiManager = (UiManager.instance as UiManager);
                uiManager.enemyDead(UUID);
            },
        };

    palBattleSystem: PalBattleSystem = null!;
    protected onLoad(): void {
        this.installSingletion();
        this.enterBattle();
    }

    // 进入战斗 方法
    enterBattle() {
        this.palBattleSystem = new PalBattleSystem();
        this.palBattleSystem.actionQueue = new ActionQueue();
        this.palBattleSystem.actionQueue.callables = this.callables;
    }

    protected start(): void {
        const 李逍遥 = (DataManager.instance as DataManager).getHero(HeroEnum.李逍遥);
        const playerManager = (PlayerManager.instance as PlayerManager);
        const playerUUID = '1';
        playerManager.setHero(playerUUID, 李逍遥.saveData);

        const 赵灵儿 = (DataManager.instance as DataManager).getHero(HeroEnum.赵灵儿);
        playerManager.setHero(playerUUID, 赵灵儿.saveData);

        const 王语嫣 = (DataManager.instance as DataManager).getHero(HeroEnum.赵灵儿);
        王语嫣.saveData.UUID = (DataManager.instance as DataManager).generatorUUID();
        playerManager.setHero(playerUUID, 王语嫣.saveData);

        const heros: PalBattleHero[] = playerManager.getHeros(playerUUID).map(hero => {
            return {
                originData: (DataManager.instance as DataManager).getHeroOriginData(hero.heroID),
                saveData: hero,
            }
        });
        this.palBattleSystem.setData([
            EnemyEnum.草妖,
            EnemyEnum.草妖,
            EnemyEnum.草妖,
        ], heros);
    }

    handlerTurnOverDebug() {
        this.palBattleSystem.nextTurn();
    }
    openSkillDialog() {
        (UiManager.instance as UiManager).skillDialog((this.palBattleSystem.getCurrentHero().saveData.UUID));
    }

    heroAttackEnemy(fromUUID: string, toUUID: string, atk: number) {
        const system = this.palBattleSystem;
        const uiManager = (UiManager.instance as UiManager);
        system.actionQueue.add({
            actions: [
                {
                    action: GameActionQueueEnum.HeroAttack,
                    duration: config.attackSpeed,
                    args: {
                        fromUUID,
                        toUUID,
                    }
                },
                {
                    action: GameActionQueueEnum.Damage,
                    duration: config.attackSpeed,
                    args: {
                        worldPosition: v3(uiManager.getEnemyNode(toUUID).worldPosition.x, uiManager.getEnemyNode(toUUID).worldPosition.y - (uiManager.getEnemyNode(toUUID).getComponent(UITransform) as UITransform).height / 2, 0),
                        atk,
                    }
                },
                {
                    action: GameActionQueueEnum.SetEnemyStatus,
                    duration: 0,
                    args: {
                        UUID: toUUID,
                        status: 'hp',
                        value: () => system.getEnemyHp(toUUID) - atk,
                    }
                },
            ],
            flag: ''
        })
    }

    enemyAttackHero(fromUUID: string, toUUID: string, atk: number) {
        const system = this.palBattleSystem;
        const uiManager = (UiManager.instance as UiManager);
        system.actionQueue.add({
            actions: [
                {
                    action: GameActionQueueEnum.EnemyAttack,
                    duration: config.attackSpeed,
                    args: {
                        fromUUID,
                        toUUID,
                    }
                },
                {
                    action: GameActionQueueEnum.Damage,
                    duration: config.attackSpeed,
                    args: {
                        worldPosition: v3(uiManager.getHeroNode(toUUID).worldPosition.x, uiManager.getHeroNode(toUUID).worldPosition.y - (uiManager.getHeroNode(toUUID).getComponent(UITransform) as UITransform).height / 2, 0),
                        atk,
                    }
                },
                {
                    action: GameActionQueueEnum.SetHeroStatus,
                    duration: 0,
                    args: {
                        UUID: toUUID,
                        status: 'hp',
                        value: () => system.getHeroHp(toUUID) - atk,
                    }
                },
            ],
            flag: ''
        })
    }

    heroDefense(fromUUID: string) {
        console.log('handlerHeroDefense', fromUUID);
        const system = this.palBattleSystem;
        system.actionQueue.add({
            actions: [
                {
                    action: GameActionQueueEnum.SetHeroStatus,
                    duration: 0,
                    args: {
                        UUID: fromUUID,
                        status: 'defense',
                        value: true,
                    }
                },
            ],
            flag: ''
        });
    }
    useSkill(skill: PalBattleSkill, targets: string[]) {
        skill.originData.callable.call(this, {
            game: this,
            system: this.palBattleSystem,
            targets: targets,
        });
        const system = this.palBattleSystem;
        if (system.currentHeroIndex == system.heros.length) {
            system.actionQueue.play();
        } else {
            system.nextHero();
        }
    }
}


